exports.__esModule = true;

var _assert = require('assert');

var _assert2 = _interopRequireDefault(_assert);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

const defaultEntity = { // 树表实体key名标识
  ID: 'id', // 主键id
  PID: 'parent_id', // 父级id
  PIDS: 'parent_ids', // 父级ids
  LEVEL: 'level', // 级别
  CHILDREN: 'children',
  LEAF: 'tree_leaf', // 是否是叶子节点
  TREE_NAMES: 'tree_names', // 全路径名称
  TREE_NAME: 'tree_name', // 节点名称
  DELETE_WAY: 1, // 删除方式：0修改状态，不删除数据;1.删除数据;
  STATUS: 'status' // 状态

};

/**
 *  Tree基类
 */
class TreeService extends think.Service {
  constructor(config) {
    super();
    Object.assign(defaultEntity, config);
  }

  /**
     * 用于树表新增和更新当前节点的所有pids,level,pid,以及所有子子孙孙的pids,　支持任意一级的更新，支持父子节点交换。
     * @param tableName  表名 string
     * @param parentEntity 父对象实体 Object
     * @param childEntity 要保存或更新的子对象实体 Object
     * @param options 选项对象
     */
  saveTree(tableName, parentEntity, childEntity, options) {
    var _this = this;

    return _asyncToGenerator(function* () {
      (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
      (0, _assert2.default)(parentEntity instanceof Object, 'parentEntity must be a Object');
      (0, _assert2.default)(childEntity instanceof Object, 'childEntity must be a Object');
      const tableModel = _this.db[tableName];
      const ID = defaultEntity.ID;
      const PID = defaultEntity.PID;
      const PIDS = defaultEntity.PIDS;
      const LEVEL = defaultEntity.LEVEL;
      const TREE_NAMES = defaultEntity.TREE_NAMES;
      const TREE_NAME = defaultEntity.TREE_NAME;
      const TREE_LEAF = defaultEntity.TREE_LEAF;
      // 获取父对象和子对象属性
      const { [ID]: pEntityId, [PID]: pEntityPid, [PIDS]: pEntityPids, [TREE_NAMES]: pEntityTreeNames = '' } = parentEntity;
      const { [ID]: cEntityId, [PID]: cEntityPid, [PIDS]: cEntityPids, [TREE_NAME]: cEntityTreeName = '' } = childEntity;
      // 获取子对象修改前的parentIds和pId，用于更新子节点的parentIds和Pid
      const oldParentIds = cEntityPids;
      const oldParentId = cEntityPid;

      // 如果目标父节点是自己，则不处理PIDS、PID、LEVEL
      if (pEntityId === cEntityId) {
        delete childEntity[PID];
        delete childEntity[PIDS];
        delete childEntity[LEVEL];
        return tableModel.upsert(childEntity);
      }
      // 如果没有设置父节点，则代表为根节点
      if (pEntityPid === null || pEntityPid === '' || pEntityPid === undefined) {
        childEntity[PID] = 0;
        childEntity[PIDS] = '0,';
      } else {
        // 获取父对象实体的parent_ids并且拼接parent_ids
        let updateParentIds = '';
        // 如果存在父子节点交换，踢出在树路径里自己的id
        if (pEntityPids && pEntityPids.includes(cEntityId)) {
          // 更新当前节点的parentIds
          updateParentIds = `${pEntityPids}${pEntityId},`.replace(`${cEntityId},`, '');
        } else {
          updateParentIds = `${pEntityPids}${pEntityId},`;
        }

        childEntity[PIDS] = updateParentIds;
        childEntity[PID] = pEntityId;
        childEntity[LEVEL] = updateParentIds.split(',').length - 1;
      }
      if (cEntityTreeName) {
        // 构造节点全路径名称
        pEntityTreeNames ? childEntity[TREE_NAMES] = pEntityTreeNames + '/' + cEntityTreeName : childEntity[TREE_NAMES] = cEntityTreeName;
      }

      // 如果是新增操作,设置当前节点为末级节点，同时设置父节点为非末级节点
      if (!cEntityId) {
        childEntity[TREE_LEAF] = 1;
        tableModel.update({ [TREE_LEAF]: 0 }, {
          where: {
            [ID]: pEntityId
          }
        });
      }
      // 保存或更新当前实体
      const upsertResult = tableModel.upsert(childEntity);

      // 更新当前修改节点下所有子节点的parentIds
      if (cEntityId && pEntityPids) {
        (0, _assert2.default)(oldParentIds != undefined, 'parentIds must be not empty');
        const sql = `SELECT ${ID},${PID},${PIDS} FROM ${tableName} WHERE ${PIDS} like '%${oldParentIds}${cEntityId},%'`;
        const list = yield _this.CrudService.querySql(sql);
        if (list.length > 0) {
          for (const value of list) {
            if (value[PIDS] && oldParentIds) {
              const obj = {};
              // 如果存在父子节点交换,踢出在树路径里自己的id
              if (pEntityPids.includes(cEntityId)) {
                obj[PIDS] = value[PIDS].replace(`${cEntityId},`, '');
                // 更新交换节点前的pid
                if (value[PID] === cEntityId) {
                  obj[PID] = oldParentId;
                }
              } else {
                obj[PIDS] = value[PIDS].replace(oldParentIds, cEntityPids);
              }
              obj[LEVEL] = obj[PIDS].split(',').length - 1;
              tableModel.update(obj, {
                where: {
                  id: value[ID]
                }
              });
            }
          }
        }
      }
      return upsertResult;
    })();
  }

  /**
     *  全表扫描，用于重新生成全表的PIDS
     *
     * @param tableName  表名 string
     */
  setAllParentIds(tableName) {
    var _this2 = this;

    return _asyncToGenerator(function* () {
      (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
      const tableModel = _this2.db[tableName];
      // 获得表中所有 数据
      const tableDataList = yield tableModel.findAll();
      // 将数组构造为id为key的对象
      const idField = defaultEntity.ID;
      const parentIDField = defaultEntity.PID;
      const parentIDSField = defaultEntity.PIDS;
      const dataObj = {};
      tableDataList.map(function (item) {
        dataObj[item[idField]] = item.dataValues;
      });
      // 遍历数组，并查出parent_ids
      tableDataList.map(function (item) {
        let parent_ids = item[parentIDField] + ',';
        let isHasParent = true;
        const parent_id = item[parentIDField];
        while (isHasParent) {
          let parentObj = {};
          parentObj = dataObj[parent_id];
          while (parentObj != undefined) {
            parent_ids = parentObj.parent_id + ',' + parent_ids;
            parentObj = dataObj[parentObj.parent_id];
          }
          isHasParent = false;
        }
        item[parentIDSField] = parent_ids;
        return item;
      });
      // 捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
      try {
        for (let i = 0; i < tableDataList.length; i++) {
          yield tableModel.update({ [parentIDSField]: tableDataList[i].dataValues[parentIDSField] }, { where: { [idField]: tableDataList[i][idField] } });
        }
      } catch (e) {
        console.log(e);
      }
    })();
  }

  /**
     *  全表扫描，用于重新生成全表的LEVEL
     *
     * @param tableName  表名 string
     */
  setLevel(tableName) {
    var _this3 = this;

    return _asyncToGenerator(function* () {
      (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
      const tableModel = _this3.db[tableName];
      // 获得表中所有 数据
      const tableDataList = yield tableModel.findAll();
      const idField = defaultEntity.ID;
      const parentIDSField = defaultEntity.PIDS;
      const levelField = defaultEntity.LEVEL;
      // 遍历数组，并查出parent_ids
      tableDataList.map(function (item) {
        const parent_idsArr = item[parentIDSField].split(',');
        item[levelField] = parent_idsArr.length - 1;
      });
      // 捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
      try {
        for (let i = 0; i < tableDataList.length; i++) {
          yield tableModel.update({ [levelField]: tableDataList[i][levelField] }, { where: { [idField]: tableDataList[i][idField] } });
        }
      } catch (e) {
        console.log(e);
      }
    })();
  }

  /**
     * 格式转树状结构
     * @param   {Array}  a 原数据
     */
  transData(a) {
    let r = [],
        hash = {},
        id = defaultEntity.ID,
        pid = defaultEntity.PID,
        children = defaultEntity.CHILDREN,
        i = 0,
        j = 0,
        len = a.length;
    for (; i < len; i++) {
      hash[a[i][id]] = a[i];
    }
    for (; j < len; j++) {
      var aVal = a[j],
          hashVP = hash[aVal[pid]];
      if (hashVP) {
        !hashVP[children] && (hashVP[children] = []);
        hashVP[children].push(aVal);
      } else {
        r.push(aVal);
      }
    }
    return r;
  }

  /**
     * 根据表名和ID获取当前节点的子子孙孙
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
  findGrandchildrens(tableName, id, fields = ['*']) {
    const { ID, PIDS } = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%')`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID获取当前节点的子子孙孙（包含自己）
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
  findGrandchildrensAndSelf(tableName, id, fields = ['*']) {
    const { ID, PIDS } = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%') or ${ID}=${id}`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID获取当前节点下的所有子节点(不包含孙孙子子)
     */
  findChildrens(tableName, id, fields = ['*']) {
    const { ID, PIDS, PID } = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PID}=${id}`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID删除当前节点及其下面的所有子节点
     */
  deleteParentAndChildrenNode(tableName, id) {
    var _this4 = this;

    return _asyncToGenerator(function* () {
      (0, _assert2.default)(typeof tableName === 'string', 'tableName must be a string');
      const tableModel = _this4.db[tableName];
      const { ID, PIDS, DELETE_WAY, STATUS } = defaultEntity;
      const Op = _this4.db.sequelize.Op;
      try {
        if (DELETE_WAY) {
          yield tableModel.destroy({
            where: {
              [Op.or]: {
                [PIDS]: {
                  [Op.like]: '%' + id + ','
                },
                [ID]: id
              }
            }
          });
        } else {
          yield tableModel.update({ [STATUS]: 1 }, {
            where: {
              [Op.or]: {
                [PIDS]: {
                  [Op.like]: '%' + id + ','
                },
                [ID]: id
              }
            }
          });
        }
      } catch (error) {
        return _this4.exceptionService.handleError(error);
      }
    })();
  }
}

exports.default = TreeService;